package cn.zyjblogs.starter.common.utils.bean;

import cn.zyjblogs.starter.common.utils.jackson.JacksonHolder;
import cn.zyjblogs.starter.common.utils.jackson.exception.JsonConvertException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import ma.glasnost.orika.DefaultFieldMapper;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

public class BeanUtils {
    private static final MapperFacade DEFAULT_MAPPER_FACADE = (new DefaultMapperFactory.Builder()).build().getMapperFacade();
    private static final Map<String, String> MYSQL_ESCAPE_MAP = new LinkedHashMap<>() {{
        put("\\", "\\\\");
        put("%", "\\%");
        put("'", "\\'");
        put("_", "\\_");
    }};


    private BeanUtils() {
    }

    public static Map<String, Object> beanToMap(Object bean) {
        Map<String, Object> result = new HashMap();
        DEFAULT_MAPPER_FACADE.map(bean, result);
        return result;
    }

    public static <S, T> T map(S sourceBean, Class<T> targetClass) {
        return DEFAULT_MAPPER_FACADE.map(sourceBean, targetClass);
    }

    public static <S, T> T map(S sourceBean, Class<T> targetClass, Map<String, String> customPropertyMap) {
        MapperFacade mapperFacade = getMapperFacadeForCustomPropertyMap(sourceBean.getClass(), targetClass, customPropertyMap);
        return mapperFacade.map(sourceBean, targetClass);
    }

    public static <S, T> T map(S sourceBean, T targetBean) {
        DEFAULT_MAPPER_FACADE.map(sourceBean, targetBean);
        return targetBean;
    }

    public static <S, T> T map(S sourceBean, T targetBean, Map<String, String> customPropertyMap) {
        MapperFacade mapperFacade = getMapperFacadeForCustomPropertyMap(sourceBean.getClass(), targetBean.getClass(), customPropertyMap);
        mapperFacade.map(sourceBean, targetBean);
        return targetBean;
    }

    public static <S, T> List<T> map(Iterable<S> sourceData, Class<T> targetClass) {
        return DEFAULT_MAPPER_FACADE.mapAsList(sourceData, targetClass);
    }

    public static <T> T mapByJackson(Object content, TypeReference<T> valueTypeRef) {
        if (!Objects.isNull(content) && !StringUtils.isBlank(content.toString())) {
            try {
                T result = JacksonHolder.MAPPER.readValue(JacksonHolder.MAPPER.writeValueAsString(content), valueTypeRef);
                return result;
            } catch (JsonProcessingException var4) {
                throw new JsonConvertException("json转换异常", var4);
            }
        } else {
            return null;
        }
    }

    public static <T> T handlePropertyForLikeQuery(T bean, Set<String> ignoreProperty) {
        if (bean != null) {
            Class<?> clazz = bean.getClass();
            Field[] fields = clazz.getDeclaredFields();
            Field[] var4 = fields;
            int var5 = fields.length;

            for (int var6 = 0; var6 < var5; ++var6) {
                Field field = var4[var6];
                String fieldName = field.getName();
                if ((ignoreProperty == null || !ignoreProperty.contains(fieldName)) && String.class == field.getType()) {
                    String originalValue = (String) getFieldValue(bean, field.getName(), String.class);
                    String newValue = escapeFieldValue(originalValue);
                    setFieldValue(bean, fieldName, newValue);
                }
            }
        }

        return bean;
    }

    public static <K, V> Map<V, K> reverseMap(Map<K, V> map) {
        return (Map) (map != null && map.size() != 0 ? (Map) map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey, (key1, key2) -> {
            return key2;
        })) : new HashMap(0));
    }

    private static <T> T getFieldValue(Object bean, String fieldName, Class<T> clazz) {
        Method method = null;

        try {
            method = bean.getClass().getMethod("get" + getFieldBaseMethodName(fieldName));
            return (T) method.invoke(bean);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException var5) {
            throw new JsonConvertException("bean转义时，获取属性值出现异常", var5);
        }
    }

    public static <T> T handlePropertyForLikeQuery(T bean) {
        return (T) handlePropertyForLikeQuery(bean, null);
    }

    private static void setFieldValue(Object bean, String fieldName, Object fieldValue) {
        Class clazz = bean.getClass();

        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(bean, fieldValue);
        } catch (IllegalAccessException | NoSuchFieldException var6) {
            throw new JsonConvertException("bean转义时，设置属性值出现异常", var6);
        }
    }

    private static String getFieldBaseMethodName(String fieldName) {
        byte[] items = fieldName.getBytes();
        items[0] = (byte) ((char) items[0] - 97 + 65);
        return new String(items);
    }

    private static String escapeFieldValue(String originalFieldValue) {
        if (originalFieldValue != null && !"".equals(originalFieldValue)) {
            String oldChar;
            String newChar;
            for (Iterator var1 = MYSQL_ESCAPE_MAP.entrySet().iterator(); var1.hasNext(); originalFieldValue = originalFieldValue.replace(oldChar, newChar)) {
                Map.Entry<String, String> escapeEntry = (Map.Entry) var1.next();
                oldChar = (String) escapeEntry.getKey();
                newChar = (String) escapeEntry.getValue();
            }

            return originalFieldValue;
        } else {
            return null;
        }
    }

    public static <S, T> MapperFacade getMapperFacadeForCustomPropertyMap(Class<S> sourceClass, Class<T> targetClass, Map<String, String> customPropertyMap) {
        MapperFactory mapperFactory = (new DefaultMapperFactory.Builder()).build();
        ClassMapBuilder<S, T> classMapBuilder = mapperFactory.classMap(sourceClass, targetClass);
        Objects.requireNonNull(classMapBuilder);
        customPropertyMap.forEach(classMapBuilder::field);
        classMapBuilder.byDefault(new DefaultFieldMapper[0]).register();
        return mapperFactory.getMapperFacade();
    }


}